#!/usr/bin/env python
# Copyright 2014, Rackspace US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import glanceclient.client as glclient
import keystoneclient.v3.client as ksclient

# import module snippets
from ansible.module_utils.basic import *


DOCUMENTATION = """
---
module: glance
short_description:
    - Basic module for interacting with openstack glance
description:
    - Basic module for interacting with openstack glance
options:
    command:
        description:
            - Operation for the module to perform. Currently available
        choices:
            - image-list
            - image-create
    openrc_path:
        decription:
            - Path to openrc file from which credentials and keystoneclient
            - endpoint will be extracted
    image_name:
        description:
            - Name of the image to create
    image_url:
        description:
            - URL from which to download the image data
    image_container_format:
        description:
            - container format that the image uses (bare)
    image_disk_format:
        description:
            - disk format that the image uses
    image_is_public:
        description:
            - Should the image be visible to all tenants?
        choices:
            - true (public)
            - false (private)
    image_properties:
        description:
            - List of properties and their values
    api_version:
        description:
            - which version of the glance api to use
        choices:
            - 1
            - 2
        default: 1
    insecure:
        description:
            - Explicitly allow client to perform "insecure" TLS
        choices:
            - false
            - true
        default: false
author: Hugh Saunders
"""

EXAMPLES = """
# Create an image
- name: Ensure cirros image
  glance:
    command: 'image-create'
    openrc_path: /root/openrc
    image_name: cirros
    image_url: 'https://example-domain.com/cirros-0.3.2-source.tar.gz'
    image_container_format: bare
    image_disk_format: qcow2
    image_is_public: True
    image_properties: { "os_distro": "cirros" }

# Get facts about existing images
- name: Get image facts
  glance:
    command: 'image-list'
    openrc_path: /root/openrc
"""


COMMAND_MAP = {'image-list': 'list_images',
               'image-create': 'create_image'}


class ManageGlance(object):
    def __init__(self, module):
        self.state_change = False
        self.glance = None
        self.keystone = None
        self.module = module
        try:
            self._keystone_authenticate()
            self._init_glance()
        except Exception as e:
            self.module.fail_json(
                err="Initialisation Error: %s" % e,
                rc=2, msg=str(e))

    def _parse_openrc(self):
        """Get credentials from an openrc file."""
        openrc_path = self.module.params['openrc_path']
        line_re = re.compile('^export (?P<key>OS_\w*)=(?P<value>[^\n]*)')
        with open(openrc_path) as openrc:
            matches = [line_re.match(l) for l in openrc]
            return dict(
                (g.groupdict()['key'], g.groupdict()['value'])
                for g in matches if g
            )

    def _keystone_authenticate(self):
        """Authenticate with Keystone."""
        openrc = self._parse_openrc()
        insecure = self.module.params['insecure']
        self.keystone = ksclient.Client(insecure=insecure,
                                        username=openrc['OS_USERNAME'],
                                        password=openrc['OS_PASSWORD'],
                                        project_name=openrc['OS_PROJECT_NAME'],
                                        auth_url=openrc['OS_AUTH_URL'])

    def _init_glance(self):
        """Create glance client object using token and url from keystone."""
        openrc = self._parse_openrc()
        p = self.module.params
        v = p['api_version']
        ep = self.keystone.service_catalog.url_for(
            service_type='image',
            endpoint_type=openrc['OS_ENDPOINT_TYPE']
        )

        self.glance = glclient.Client(
            endpoint='%s/v%s' % (ep, v),
            token=self.keystone.get_token(self.keystone.session)
        )

    def route(self):
        """Run the command specified by the command parameter."""
        getattr(self, COMMAND_MAP[self.module.params['command']])()

    def _get_image_facts(self):
        """Helper function to format image list as a dictionary."""
        p = self.module.params
        v = p['api_version']
        if v == '1':
            return dict(
                (i.name, i.to_dict()) for i in self.glance.images.list()
            )
        elif v == '2':
            return dict(
                (i.name, i) for i in self.glance.images.list()
            )

    def list_images(self):
        """Get information about available glance images.

        Returns as a fact dictionary glance_images
        """
        self.module.exit_json(
            changed=self.state_change,
            ansible_facts=dict(glance_images=self._get_image_facts()))

    def create_image(self):
        """Create a glance image that references a remote url."""
        p = self.module.params
        v = p['api_version']
        image_name = p['image_name']
        image_opts = dict(
            name=image_name,
            disk_format=p['image_disk_format'],
            container_format=p['image_container_format'],
            copy_from=p['image_url']
        )
        if p['image_properties']:
            image_opts['properties'] = p['image_properties']
        if v == '1':
            image_opts['is_public'] = p['image_is_public']
        elif v == '2':
            if p['image_is_public']:
                vis = 'public'
            else:
                vis = 'private'
            image_opts['visibility'] = vis

        images = {i.name for i in self.glance.images.list()}
        if image_name in images:
            self.module.exit_json(
                changed=self.state_change,
                ansible_facts=dict(
                    glance_images=self._get_image_facts()
                )
            )
        else:
            self.glance.images.create(**image_opts)
            self.state_change = True
            self.module.exit_json(
                changed=self.state_change,
                ansible_facts=dict(
                    glance_images=self._get_image_facts()
                )
            )


def main():
    module = AnsibleModule(
        argument_spec=dict(
            command=dict(required=True, choices=COMMAND_MAP.keys()),
            openrc_path=dict(required=True),
            image_name=dict(required=False),
            image_url=dict(required=False),
            image_container_format=dict(required=False),
            image_disk_format=dict(required=False),
            image_is_public=dict(required=False, choices=BOOLEANS),
            image_properties=dict(required=False),
            api_version=dict(default='1', required=False, choices=['1', '2']),
            insecure=dict(default=False, required=False,
                          choices=BOOLEANS + ['True', 'False'])
        ),
        supports_check_mode=False
    )
    mg = ManageGlance(module)
    mg.route()

if __name__ == '__main__':
    main()
